#include "ffz/raft/raft-handler.h"
#include "ffz/raft/raft-packet.h"
#include "ffz/raft/raft-role.h"
#include "ffz/raft/raft-timer.h"
#include "ffz/ffzconfig.h"
#define DEBUG DEBUG_PRINT
#include "ffz/debug.h"
void raftHandler(const uip_ipaddr_t *sender_addr, const uint8_t *data, uint16_t datalen)
{
    uint8_t rpc_type;
    rpc_type = getRPCType(data);
    if (rpc_type == RPC_CANDIDATE)
    {
        uint8_t result;
        uint16_t term, candidateId, lastLogIndex, lastLogTerm;
        extractRaftPacketCandidate(&term, &candidateId, &lastLogIndex, &lastLogTerm, data);
        PRINTF("RPC_CANDIDATE term=%u candidate=%u logIndex=%u logTerm=%u \n", term, candidateId, lastLogIndex, lastLogTerm);
        result = RPC_ValidateChecking(term, candidateId, lastLogIndex, lastLogTerm);
        if (result)
        {
            //if the node has voted for the candidate, the timer will be reset
            resetClockCount(COUNTER_INDEX_CANDIDATE);
            RPC_VoteFor(term, candidateId);
        }
        //the term is not available
        createRaftPacketCandidateResp(getCurrentTerm(), result, raftTableLocalId(), getSendBuffer());
        raftPacketSendUnicast(getSendBuffer(), 6, sender_addr);
    }
    else if (rpc_type == RPC_CANDIDATE_RESP)
    {
        uint8_t voted;
        uint16_t term;
        uint16_t senderId;
        extractRaftPacketCandidateResp(&term, &voted, &senderId, data);
        PRINTF("RPC_CANDIDATE_RESP term=%u voted=%u \n", term, voted);
        if (term > getCurrentTerm())
        {
            ROLE_ToFollower(term);
            return;
        }
        if (voted)
        {
            RPC_GetVote();
        }
    }
    else if (rpc_type == RPC_HEARTBEATS)
    {
        uint16_t term;
        uint16_t leaderId;
        uint16_t prevIndex;
        uint16_t prevTerm;
        uint16_t commitIndex;
        uint8_t hasEntry;
        RaftLog entry;
        extractRaftPacketHeartbeats(&term, &leaderId, &prevIndex, &prevTerm, &commitIndex, &hasEntry, &entry, data);
        PRINTF("RPC_HEARTBEATS term=%u leaderId=%u prevIndex=%u prevTerm=%u commitIndex=%u hasEntry=%u  index=%u term=%u status=%u action=%u \n", term, leaderId, prevIndex, prevTerm, commitIndex, hasEntry, entry.index, entry.term, entry.status, entry.action);
        // only the FOLLOWER & CANDIDATE could finish this task
        uint8_t result = receiveHeartBeats(term, leaderId, prevIndex, prevTerm, commitIndex, hasEntry, &entry);
        resetClockCount(COUNTER_INDEX_CANDIDATE);
        createRaftPacketHeartbeatsResp(getCurrentTerm(), result, getLastLogIndex(), raftTableLocalId(), getSendBuffer());
        raftPacketSendUnicast(getSendBuffer(), 8, sender_addr);
    }
    else if (rpc_type == RPC_HEARTBEATS_RESP)
    {
        uint16_t term;
        uint8_t success;
        uint16_t matchIndex;
        uint16_t senderId;
        extractRaftPacketHeartbeatsResp(&term, &success, &matchIndex, &senderId, data);
        PRINTF("RPC_HEARTBEATS term=%u success=%u matchIndex=%u senderId=%u\n", term, success, matchIndex, senderId);
        receiveHeartBeatsResp(term, success, matchIndex, senderId);
    }
}